/*
 * Amadeus Confidential Information:
 * Unauthorized use and disclosure strictly forbidden.
 * @1998-2009 - Amadeus s.a.s - All Rights Reserved.
 */
package com.amadeus.spin.devtool.soap.shooter.config;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.amadeus.spin.devtool.soap.shooter.FatalException;
import com.amadeus.spin.devtool.soap.shooter.filter.FilterHandler;
import com.amadeus.spin.standard.util.EncodingUtils;
import com.amadeus.spin.standard.util.FileUtils;

public class ConfigurationParameters {

  // Singleton Pattern
  private static volatile ConfigurationParameters instance = null;

  private static Logger logger = Logger.getLogger(ConfigurationParameters.class);

  private static String replaceXmlString = null;

  private static String forceOverrideXmlString = null;

  private static String scenarioFileName;

  private static String scenarioProcessed;

  private static String paramsFileName;

  private static String inputDir;

  private static String relativeInputDir;

  private static String relativeOutputDir;

  private static String outputDir;

  private static String traceDir;

  private static String server;

  private static String router;

  private static String protocol = "http://";

  private static String fullTargetPath;

  private static String username;

  private static boolean sessionLess;

  private static String[] optionFilters = new String[0];;

  private static String password;

  private static String serviceid;

  private static String proxyhost;

  private static int proxyport = -1;

  private static String proxyUserName;

  private static String proxyPassword;

  private static String sessionId;

  private static boolean uRLRewriting = false;

  private static int timeout = 0;

  private static boolean dumpSoapTrace = true;

  private static boolean dumpOutput = true;

  private static String hosttracesPath = "";

  private static boolean zip = false;

  private static boolean verbose = false;

  // Cleanup mechanism for output files that or more than a given time limit old
  // (set to 1h).
  private static final long TIME_OF_VALIDITY = 60 * 1000;

  private static final String SERVER_DEFAULT_VERSION = "11.2";

  private static final String SERVER_DEFAULT_STREAM_NAME = "int";

  private static String serverVersion = SERVER_DEFAULT_VERSION;

  private static String serverStreamName = SERVER_DEFAULT_STREAM_NAME;

  private static Factory factory;

  private static FilterHandler filterHandler;

  private static String encoding;

  private static String comment;

  // scenario config file helper
  private static String ctxRecordReplayFilename;

  private static boolean allowManual = true;

  private static String soapshooterVersion = null;

  /**
   * Output directory value which should override what's defined in the scenario
   * file if it's non null.
   */
  private static String overridenOutputDirValue = null;

  /**
   * Indicate which schema should by used to validate soap request (original one defined in the soap request, schema
   * from the targeted server)
   */
  private static boolean useTargetServerSchema = false;

  /**
   * The version
   */
  private static String customSchemaVersion = "";

  /**
   * Additional parameters to the url
   */
  private static String additionalUrlParams = null;

  /**
   * The Janus Id used for Mocking
   */
  private static String janusId = null;

  /**
   * Indicates if the schema version should be overwritten by the input parameter
   */
  private static boolean useCustomSchemaVersion = false;

  protected ConfigurationParameters() {
  }

  public static ConfigurationParameters getInstance() {
    if (instance == null) {
      instance = new ConfigurationParameters();
    }
    return instance;
  }

  /**
   * Updates some parameters, such as directory entries, protocol, default
   * server, etc.
   */
  public static void updateConfigurationParameters() {
    inputDir = FileUtils.getNormalizedDirName(inputDir);
    outputDir = FileUtils.getNormalizedDirName(outputDir);
    scenarioFileName = scenarioFileName.replaceAll("\\\\", "/");
    if (traceDir == null) {
      traceDir = "./soaptrace";
    }
    else {
      traceDir = FileUtils.getNormalizedDirName(traceDir);
    }

    if (server == null) {
      server = "localhost:7001";
    }
    if (router == null) {
      router = "SOAPRPCRouterServlet";
    }
    // make sure the parameters are following the expected syntax
    // and build the full path which will used to shoot the requests
    server = server.trim();
    if (server.toLowerCase().startsWith("https://")) {
      protocol = "https://";
    }
    if (!server.endsWith("/")) {
      fullTargetPath = server + "/";
    }
    if (!server.toLowerCase().startsWith(protocol)) {
      fullTargetPath = protocol + fullTargetPath;
    }
    if (!router.startsWith("/")) {
      fullTargetPath = fullTargetPath + router;
    }
    else {
      fullTargetPath = fullTargetPath + router.substring(1, router.length());
    }

    loadSoapShooterVersion();
  }

  /**
   * Reads the manifest.mf file to retrieve the soapshooter version.
   * If we were not using 1.0-SNAPSHOT versions, this could be easily replaced by a call to
   * getClass().getPackage().getImplementationVersion()
   * 
   */
  private static void loadSoapShooterVersion() {
    try {
      Enumeration<URL> resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
      URL url = null;

      while (resEnum.hasMoreElements()) {
        try {
          url = resEnum.nextElement();
          InputStream is = url.openStream();
          if (is != null) {
            Manifest manifest = new Manifest(is);
            Attributes mainAttribs = manifest.getMainAttributes();
            String version = mainAttribs.getValue("SoapShooter-Version");
            if (version != null) {
              soapshooterVersion = version;
            }
          }
        }
        catch (Exception e) {
          logger.debug("Invalid Manifest at URL '" + url + "' (this message is not an error)");
        }
      }
    }
    catch (IOException e1) {
      logger.debug("Can't retrieve " + JarFile.MANIFEST_NAME + " for shoapshooter jar");
    }
  }

  public static void setInputDir(String paramInputDir) throws FatalException {
    relativeInputDir = paramInputDir;
    scenarioFileName = scenarioFileName.replaceAll("\\\\", "/");
    String scenarioDir = StringUtils.substringBeforeLast(scenarioFileName, "/");

    String fullPath = scenarioDir + '/' + paramInputDir;
    File tempDir = new File(fullPath);
    if (!tempDir.exists() || (tempDir.exists() && !tempDir.isDirectory())) {
      throw new FatalException("Input directory, '" + paramInputDir + "' does not exists or is not a directory.");
    }
    inputDir = fullPath;
  }

  public static String getInputDir() {
    return inputDir;
  }

  public static String getRelativeInputDir() {
    return relativeInputDir;
  }

  /**
   * Set the output directory to outputDir if no override parameter has been specified. Constraints:
   * <ul>
   * <li>relative directories should be relative to the scenario file directory, not the directory where soapshooter is
   * run</li>
   * <li>output directory specified in command line override the one defines in the scenario</li>
   * <li>output directory specified in command line can be absolute</li>
   * <li>output directory specified in scenario is relative (should we support absolute dir in scenario?)</li>
   * </ul>
   * Please feel free to refactor.
   * 
   * @param paramOutputDir
   * @throws FatalException
   */
  public static void setOutputDir(String paramOutputDir) throws FatalException {
    if (overridenOutputDirValue == null) {
      relativeOutputDir = paramOutputDir;
    }
    else {
      relativeOutputDir = overridenOutputDirValue;
    }

    // We open a file to given relative outputdir - just to check wheter it's an
    // absolute file path or not
    File tempDir = new File(relativeOutputDir);
    String fullPath = "";
    if (tempDir.isAbsolute()) {
      fullPath = relativeOutputDir;
    }
    else {
      fullPath = StringUtils.substringBeforeLast(scenarioFileName, "/") + '/' + relativeOutputDir;

      tempDir = new File(fullPath);
      if (tempDir.exists() && !tempDir.isDirectory()) {
        throw new FatalException("Problem creating output directory '" + relativeOutputDir +
            "' : a file already exists at this location.");
      }
      if (!tempDir.exists()) {
        tempDir.mkdir();
      }
    }
    outputDir = fullPath;
  }

  public static String getOutputDir() {
    return outputDir;
  }

  public static String getRelativeOutputDir() {
    return relativeOutputDir;
  }

  public static void setTraceDir(String traceDir) {
    ConfigurationParameters.traceDir = traceDir;
  }

  public static String getTraceDir() {
    return traceDir;
  }

  /**
   * sets the server and formats it so that it doesn't end with a slash
   * 
   * @param server
   */
  public static void setServer(String server) {
    server = StringUtils.removeEnd(server, "/");
    server = StringUtils.removeEnd(server, "\\");
    // server = StringUtils.removeStartIgnoreCase(server, "http://");
    ConfigurationParameters.server = server;

  }

  public static String getServer() {
    return server;
  }

  public static void setRouter(String router) {
    ConfigurationParameters.router = router;
  }

  public static String getRouter() {
    return router;
  }

  public static void setProtocol(String protocol) {
    ConfigurationParameters.protocol = protocol;
  }

  public static String getProtocol() {
    return protocol;
  }

  public static String getFullTargetPath() {
    return fullTargetPath;
  }

  public static String getSoapShooterVersion() {
    if (soapshooterVersion == null) {
      loadSoapShooterVersion();
    }
    return soapshooterVersion;
  }

  public static void setFullTargetPath(String fullTargetPath) {
    ConfigurationParameters.fullTargetPath = fullTargetPath;
  }

  public static String getUsername() {
    return username;
  }

  public static void setUsername(String username) {
    ConfigurationParameters.username = username;
  }

  public static String getPassword() {
    return password;
  }

  public static void setPassword(String password) {
    ConfigurationParameters.password = password;
  }

  public static String getServiceid() {
    return serviceid;
  }

  public static void setServiceid(String serviceid) {
    ConfigurationParameters.serviceid = serviceid;
  }

  public static String getProxyhost() {
    return proxyhost;
  }

  public static void setProxyhost(String proxyhost) {
    ConfigurationParameters.proxyhost = proxyhost;
  }

  public static String getProxyUserName() {
    return proxyUserName;
  }

  public static void setProxyUserName(String proxyUserName) {
    ConfigurationParameters.proxyUserName = proxyUserName;
  }

  public static String getProxyPassword() {
    return proxyPassword;
  }

  public static void setProxyPassword(String proxyPassword) {
    ConfigurationParameters.proxyPassword = proxyPassword;
  }

  public static String getSessionId() {
    return sessionId;
  }

  public static void setSessionId(String sessionId) {
    ConfigurationParameters.sessionId = sessionId;
  }

  public static boolean getURLRewriting() {
    return uRLRewriting;
  }

  public static void setURLRewriting(boolean rewriting) {
    uRLRewriting = rewriting;
  }

  public static void setTimeout(int timeout) {
    ConfigurationParameters.timeout = timeout;
  }

  public static int getTimeout() {
    return timeout;
  }

  public static void setProxyport(int proxyport) {
    ConfigurationParameters.proxyport = proxyport;
  }

  public static int getProxyport() {
    return proxyport;
  }

  public static void setDumpSoapTrace(boolean dumpSoapTrace) {
    ConfigurationParameters.dumpSoapTrace = dumpSoapTrace;
  }

  public static boolean isDumpSoapTrace() {
    return dumpSoapTrace;
  }

  public static void setDumpOutput(boolean dumpOutput) {
    ConfigurationParameters.dumpOutput = dumpOutput;
  }

  public static boolean isDumpOutput() {
    return dumpOutput;
  }

  public static void setVerbose(boolean verbose) {
    ConfigurationParameters.verbose = verbose;
  }

  public static boolean isVerbose() {
    return verbose;
  }

  public static long getTimeOfValidityInMS() {
    return TIME_OF_VALIDITY;
  }

  public static void setServerVersionUsed(String serverVersionUsed) {
    ConfigurationParameters.serverVersion = serverVersionUsed;
  }

  public static String getServerVersionUsed() {
    return serverVersion;
  }

  public static void setServerStreamName(String serverStreamName) {
    ConfigurationParameters.serverStreamName = serverStreamName;
  }

  public static String getServerStreamName() {
    return serverStreamName;
  }

  public static void setFactory(Factory factory) {
    ConfigurationParameters.factory = factory;
  }

  public static Factory getFactory() {
    return factory;
  }

  public static void setFilterHandler(FilterHandler filterHandler) {
    ConfigurationParameters.filterHandler = filterHandler;
  }

  public static FilterHandler getFilterHandler() {
    return filterHandler;
  }

  public static boolean isRetrieveHostTraces() {
    return !StringUtils.isEmpty(hosttracesPath);
  }

  public static void setHosttracesPath(String hosttracesPath) {
    ConfigurationParameters.hosttracesPath = hosttracesPath;
  }

  public static String getHosttracesPath() {
    return hosttracesPath;
  }

  public static void setReplaceXmlString(String replaceXmlString) {
    ConfigurationParameters.replaceXmlString = replaceXmlString;
  }

  public static String getReplaceXmlString() {
    return replaceXmlString;
  }

  public static void setZip(boolean zip) {
    ConfigurationParameters.zip = zip;
  }

  public static boolean isZip() {
    return zip;
  }

  public static void setCtxRecordReplayFilename(String ctxRecordReplayFilename) {
    ConfigurationParameters.ctxRecordReplayFilename = ctxRecordReplayFilename;
  }

  public static String getCtxRecordReplayFilename() {
    return ctxRecordReplayFilename;
  }

  public static void setScenarioFileName(String scenarioFileName)
      throws FatalException {
    File scenarioFile = new File(scenarioFileName);
    if (scenarioFile.exists() && scenarioFile.isFile()) {
      ConfigurationParameters.scenarioFileName = scenarioFile.getAbsolutePath();
    }
    else {
      throw new FatalException(scenarioFileName + " cannot be found or is not a file.");
    }

  }

  public static String getScenarioFileName() {
    return scenarioFileName;
  }

  public static void setScenarioProcessed(String paramScenarioProcessed) {
    scenarioProcessed = paramScenarioProcessed;
  }

  public static String getScenarioProcessed() {
    return scenarioProcessed;
  }

  public static void setParamsFileName(String paramsFileName) throws FatalException {
    File paramsFile = new File(paramsFileName);
    if (paramsFile.exists() && paramsFile.isFile()) {
      ConfigurationParameters.paramsFileName = paramsFile.getAbsolutePath();
    }
    else {
      throw new FatalException(paramsFileName + " cannot be found or is not a file.");
    }

  }

  public static String getParamsFileName() {
    return paramsFileName;
  }

  // TODO : "un-duplicate" code
  // we should add a "testdirectory" parameter
  // and/or a "testname" parameter in conf.
  public static String getReportFilePath() {
    String result = "";
    StringBuffer pathToScenarioFilewithoutExtension = new StringBuffer();
    String scenarioFileNameOnly = new File(ConfigurationParameters.getScenarioFileName()).getName();
    pathToScenarioFilewithoutExtension.append(new File(ConfigurationParameters.getScenarioFileName()).getParent());
    pathToScenarioFilewithoutExtension.append(File.separatorChar);
    pathToScenarioFilewithoutExtension.append(StringUtils.substringBefore(scenarioFileNameOnly, "."));
    pathToScenarioFilewithoutExtension.append(".out");
    result = pathToScenarioFilewithoutExtension.toString();
    File fOut = new File(result);
    if (fOut.exists()) {
      fOut.delete();
    }
    return result;
  }

  /**
   * sets the static encoding variable of the scenario
   * 
   * @throws FatalException
   */
  public static void setEncoding() {
    try {
      encoding = EncodingUtils.getFileEncoding(scenarioFileName);
    }
    catch (IOException e) {
      logger.info("Error while trying to set encoding of " + scenarioFileName + ". Leaving default : " +
          e.getMessage());
    }
  }

  /**
   * gives the encoding of the scenario
   * 
   * @return
   */
  public static String getEncoding() {
    return encoding;
  }

  public static void setComment(String comment) {
    ConfigurationParameters.comment = comment;
  }

  public static String getComment() {
    return StringUtils.trimToEmpty(comment);
  }

  public static String getForceOverrideInput() {
    return forceOverrideXmlString;
  }

  public static void setForceOverrideInput(String string) {
    forceOverrideXmlString = string;
  }

  public static boolean isForceOverrideInput() {
    return forceOverrideXmlString != null;
  }

  public static void setAllowManual(boolean isAllowManualScenario) {
    allowManual = isAllowManualScenario;
  }

  public static boolean getAllowManual() {
    return allowManual;
  }

  /**
   * Sets the output directory value which should override what's defined in the scenario file.
   * 
   * @param paramOutputDir
   */
  public static void setOverridenOutputDir(String paramOutputDir) {
    overridenOutputDirValue = paramOutputDir;
  }

  public static boolean isUseTargetServerSchema() {
    return useTargetServerSchema;
  }

  public static void setUseTargetServerSchema(boolean paramUseTargetServerSchema) {
    useTargetServerSchema = paramUseTargetServerSchema;
  }

  public static void setUseTargetServerSchema() {
    useTargetServerSchema = true;
  }

  public static void setUseOriginalServerSchema() {
    useTargetServerSchema = false;
  }

  public static boolean isUseCustomSchemaVersion() {
    return useCustomSchemaVersion;
  }

  public static void setUseCustomSchemaVersion() {
    useCustomSchemaVersion = true;
  }

  public static String getCustomSchemaVersion() {
    return customSchemaVersion;
  }

  public static void setCustomSchemaVersion(String mCustomSchemaVersion) {
    ConfigurationParameters.setUseCustomSchemaVersion();
    ConfigurationParameters.customSchemaVersion = mCustomSchemaVersion;
  }

  public static void setUrlAdditionalParams(String optionValue) {
    additionalUrlParams = optionValue;
  }

  public static String getUrlAdditionalParams() {
    return additionalUrlParams;
  }

  public static String getJanusId() {
    return janusId;
  }

  public static void setJanusId(String janusId) {
    ConfigurationParameters.janusId = janusId;
  }

  public static boolean isSessionLess() {
    return sessionLess;
  }

  public static void setSessionLess(boolean sessionLess) {
    ConfigurationParameters.sessionLess = sessionLess;
  }

  public static void setOptionFilters(String[] filters) {
    if (filters != null) {
      optionFilters = filters.clone();
    }
  }

  /**
   * Gets the list of filters defined in command line.
   * 
   * @return
   */
  public static String[] getOptionFilters() {
    return optionFilters.clone();
  }

}
